#ifndef CMS_HH
#define CMS_HH

#ifdef __cplusplus
extern "C" {
#endif

#include <stddef.h> /* size_t */

#ifdef __cplusplus
}
#endif
#include "cms_cfg.hh" /* CMS_CONFIG_LINELEN */

class PHYSMEM_HANDLE;
struct PM_CARTESIAN;
struct PM_CYLINDRICAL;
struct PM_EULER_ZYX;
struct PM_EULER_ZYZ;
struct PM_HOMOGENEOUS;
struct PM_POSE;
struct PM_QUATERNION;
struct PM_ROTATION_MATRIX;
struct PM_ROTATION_VECTOR;
struct PM_RPY;
struct PM_SPHERICAL;
class LinkedList;

enum CMS_STATUS
{
    /* ERROR conditions */
    CMS_MISC_ERROR = -1,                 /* A miscellaneous error occured. */
    CMS_UPDATE_ERROR = -2,               /* An error occured during an update. */
    CMS_INTERNAL_ACCESS_ERROR = -3,      /* An error occured during an
					   internal access function. */
    CMS_NO_MASTER_ERROR = -4,            /* An error occured becouse the master was
				   not started */
    CMS_CONFIG_ERROR = -5,               /* There was an error in the configuration */
    CMS_TIMED_OUT = -6,                  /* operation timed out. */
    CMS_QUEUE_FULL = -7,                 /* A write failed because queuing was enabled 
				   but there was no room to add to the queue. 
				 */
    CMS_CREATE_ERROR = -8,               /* Something could not be created.  */
    CMS_PERMISSIONS_ERROR = -9,          /* Problem with permissions */
    CMS_NO_SERVER_ERROR = -10,           /* The server has not been started or could
				   not be contacted. */
    CMS_RESOURCE_CONFLICT_ERROR = -11,   /* Two or more CMS buffers are trying 
					   to use the same resource. */
    CMS_NO_IMPLEMENTATION_ERROR = -12,   /* An operation was attempted which
					   has not yet been implemented for
					   the current platform or protocol. */
    CMS_INSUFFICIENT_SPACE_ERROR = -13,  /* The size of the buffer was
					   insufficient for the requested
					   operation. */
    CMS_LIBRARY_UNAVAILABLE_ERROR = -14, /* A DLL or Shared Object
						   library needed for the
						   current protocol could not 
						   be found or initialized. */
    CMS_SERVER_SIDE_ERROR = -15,         /* The server reported an error. */
    CMS_NO_BLOCKING_SEM_ERROR = -16,     /* A blocking_read operartion was
					   tried but no semaphore for the
					   blocking was configured or
					   available. */

    /* NON Error Conditions.*/
    CMS_STATUS_NOT_SET = 0,    /* The status variable has not been set yet. */
    CMS_READ_OLD = 1,          /* Read successful, but data is old. */
    CMS_READ_OK = 2,           /* Read successful so far. */
    CMS_WRITE_OK = 3,          /* Write successful so far. */
    CMS_WRITE_WAS_BLOCKED = 4, /* Write if read did not succeed, because the 
				   buffer had not been read yet. */
    CMS_CLEAR_OK = 5,          /* A clear operation was successful.  */
    CMS_CLOSED = 6             /* The channel has been closed.  */
};

/* Mode used within update functions. */
enum CMSMODE
{
    CMS_NOT_A_MODE = 0,
    CMS_ENCODE,
    CMS_DECODE,
    CMS_RAW_OUT,
    CMS_RAW_IN,
    CMS_READ,
    CMS_WRITE
};

typedef long int CMSID;

/* Mode stored for use by the internal access function. */
enum CMS_INTERNAL_ACCESS_TYPE
{
    CMS_ZERO_ACCESS = 0,
    CMS_READ_ACCESS,
    CMS_CHECK_IF_READ_ACCESS,
    CMS_PEEK_ACCESS,
    CMS_WRITE_ACCESS,
    CMS_WRITE_IF_READ_ACCESS,
    CMS_CLEAR_ACCESS,
    CMS_GET_MSG_COUNT_ACCESS,
    CMS_GET_DIAG_INFO_ACCESS,
    CMS_GET_QUEUE_LENGTH_ACCESS,
    CMS_GET_SPACE_AVAILABLE_ACCESS
};

/* What type of global memory buffer. */
enum CMS_BUFFERTYPE
{
    CMS_SHMEM_TYPE,
    CMS_PHANTOM_BUFFER,
    CMS_LOCMEM_TYPE,
    CMS_FILEMEM_TYPE,
};

/* How will this process access the buffer. */
enum CMS_PROCESSTYPE
{
    CMS_REMOTE_TYPE,
    CMS_LOCAL_TYPE,
    CMS_PHANTOM_USER,
    CMS_AUTO_TYPE
};

enum CMS_REMOTE_PORT_TYPE
{
    CMS_NO_REMOTE_PORT_TYPE = 0,
    CMS_TTY_REMOTE_PORT_TYPE,
    CMS_TCP_REMOTE_PORT_TYPE,
    CMS_STCP_REMOTE_PORT_TYPE,
    CMS_UDP_REMOTE_PORT_TYPE
};

/* This structure will be placed at the beginning of every CMS buffer. */
struct CMS_HEADER
{
    long was_read;       /* Has the buffer been read since the last
				   write? */
    long write_id;       /* Id of last write. */
    long in_buffer_size; /* How much of the buffer is currently used. */
};

class CMS_DIAG_PROC_INFO;
class CMS_DIAG_HEADER;
class CMS_DIAGNOSTICS_INFO;

struct CMS_QUEUING_HEADER
{
    long head;
    long tail;
    long queue_length;
    long end_queue_space;
    long write_id;
};

enum CMS_NEUTRAL_ENCODING_METHOD
{
    CMS_NO_ENCODING,
    CMS_XDR_ENCODING,
    CMS_ASCII_ENCODING,
    CMS_DISPLAY_ASCII_ENCODING
};

/* CMS class declaration. */
class CMS;
class CMS_UPDATER;

/* CMS class definition. */
class CMS
{
public:
    void* operator new(size_t);
    void operator delete(void*);

public:
    /* Constructors and Destructors. */
    CMS(long size);
    CMS(const char* bufline, const char* procline, int set_to_server = 0);
    virtual ~CMS();

    /* Simple read/write interface functions. */
    virtual CMS_STATUS clear();                                                   /* Has the buffer been read recently? */
    virtual int check_if_read();                                                  /* Has the buffer been read recently? 
					 */
    virtual int get_msg_count();                                                  /* Has the buffer been read recently? 
					 */
    virtual CMS_STATUS read();                                                    /* Read from buffer. */
    virtual CMS_STATUS blocking_read(double _timeout);                            /* Read from buffer,
							   wait for new data. 
							 */
    virtual CMS_STATUS peek();                                                    /* Read without setting flag. */
    virtual CMS_STATUS write(void* user_data, int* serial_number = NULL);         /* Write to buffer. */
    virtual CMS_STATUS write_if_read(void* user_data, int* serial_number = NULL); /* Write to buffer. */
    virtual int login(const char* name, const char* passwd);
    virtual void reconnect();
    virtual void disconnect();
    virtual int get_queue_length();
    virtual int get_space_available();

    /* Protocol Defined Virtual Function Stubs. */
    virtual CMS_STATUS main_access(void* _local, int* serial_number = NULL);

    /* Neutrally Encoded Buffer positioning functions. */
    void rewind();              /* positions at beginning */
    int get_encoded_msg_size(); /* Store last position in header.size */

    /* Buffer access control functions. */
    void set_mode(CMSMODE im); /* Determine read/write mode.(check neutral) */

    /* Select a temporary updator -- This is used by the nml msg2string and
       string2msg functions. */
    void set_temp_updater(CMS_NEUTRAL_ENCODING_METHOD);

    /* Restore the normal update. */
    void restore_normal_updater();

    /*******************************************************/
    /* CMS INTERNAL ACCESS FUNCTIONS located in cms_in.cc */
    /*******************************************************/
    CMS_STATUS internal_access(PHYSMEM_HANDLE* _global, void* _local, int* serial_number);
    CMS_STATUS internal_access(void* _global, long global_size, void* _local, int* serial_number);
    CMS_STATUS internal_clear(); /* Zero the global memory.  */
    int check_if_read_raw();
    int check_if_read_encoded();
    int get_msg_count_raw();
    int get_msg_count_encoded();
    CMS_STATUS read_raw();                                             /* Read from raw buffers. */
    CMS_STATUS read_encoded();                                         /* Read from neutrally encoded buffers. */
    CMS_STATUS peek_raw();                                             /* Read without setting flags. */
    CMS_STATUS peek_encoded();                                         /* Read without setting flags. */
    CMS_STATUS write_raw(void* user_data, int* serial_number);         /* Write to raw buffers. */
    CMS_STATUS write_encoded();                                        /* Write to neutrally encoded buffers. */
    CMS_STATUS write_if_read_raw(void* user_data, int* serial_number); /* Write if read. */
    CMS_STATUS write_if_read_encoded();                                /* Write if read. */
    int queue_check_if_read_raw();
    int queue_check_if_read_encoded();
    int queue_get_msg_count_raw();
    int queue_get_msg_count_encoded();
    int queue_get_queue_length_raw();
    int queue_get_queue_length_encoded();
    int queue_get_space_available_raw();
    int queue_get_space_available_encoded();
    CMS_STATUS queue_read_raw();                                     /* Read from raw buffers. */
    CMS_STATUS queue_read_encoded();                                 /* Read from neutral buffers. */
    CMS_STATUS queue_peek_raw();                                     /* Read without setting flags. */
    CMS_STATUS queue_peek_encoded();                                 /* Read without setting flags. */
    CMS_STATUS queue_write_raw(void* user_data, int* serial_number); /* Write to raw bufs */
    CMS_STATUS queue_write_encoded();                                /* Write to neutral buffers. */
    CMS_STATUS queue_write_if_read_raw(void* user_data, int* serial_number);
    CMS_STATUS queue_write_if_read_encoded(); /* Write if read. */
    virtual void clean_buffers();

    /***********************************************/
    /* CMS UPDATE FUNCTIONS located in cms_up.cc */
    /***********************************************/
    /* Access functions for primitive C language data types */
    CMS_STATUS update(bool& x);
    CMS_STATUS update(char& x);          /* Used by zuc */
    CMS_STATUS update(unsigned char& x); /* Used by zuc */
    CMS_STATUS update(short int& x);
    CMS_STATUS update(unsigned short int& x);
    CMS_STATUS update(int& x); /* Used by zuc */
    CMS_STATUS update(unsigned int& x);
    CMS_STATUS update(long int& x);          /* Used by zuc */
    CMS_STATUS update(unsigned long int& x); /* Used by zuc */
    CMS_STATUS update(float& x);
    CMS_STATUS update(double& x); /* Used by zuc */
    CMS_STATUS update(long double& x);
    CMS_STATUS update(char* x, unsigned int len);          /* Used by zuc */
    CMS_STATUS update(unsigned char* x, unsigned int len); /* Used by zuc */
    CMS_STATUS update(short* x, unsigned int len);
    CMS_STATUS update(unsigned short* x, unsigned int len);
    CMS_STATUS update(int* x, unsigned int len); /* Used by zuc */
    CMS_STATUS update(unsigned int* x, unsigned int len);
    CMS_STATUS update(long* x, unsigned int len);
    CMS_STATUS update(unsigned long* x, unsigned int len);
    CMS_STATUS update(float* x, unsigned int len);
    CMS_STATUS update(double* x, unsigned int len); /* Used by zuc */
    CMS_STATUS update(long double* x, unsigned int len);

    /*************************************************************************
   * CMS UPDATE FUNCTIONS for POSEMATH classes, defined in cms_pm.cc       *
   ************************************************************************/
    // translation types
    CMS_STATUS update(PM_CARTESIAN& x);    // Cart /* Used by zuc */
    CMS_STATUS update(PM_SPHERICAL& x);    // Sph
    CMS_STATUS update(PM_CYLINDRICAL& x);  // Cyl

    // rotation types
    CMS_STATUS update(PM_ROTATION_VECTOR& x);  // Rot
    CMS_STATUS update(PM_ROTATION_MATRIX& x);  // Mat
    CMS_STATUS update(PM_QUATERNION& x);       // Quat
    CMS_STATUS update(PM_EULER_ZYZ& x);        // Zyz
    CMS_STATUS update(PM_EULER_ZYX& x);        // Zyx
    CMS_STATUS update(PM_RPY& x);              // Rpy

    // pose types
    CMS_STATUS update(PM_POSE& x);         // Pose
    CMS_STATUS update(PM_HOMOGENEOUS& x);  // Hom

    // CMS UPDATE FUNCTIONS for arrays of POSEMATH types.
    // translation types
    CMS_STATUS update(PM_CARTESIAN* x, int n);    // Cart
    CMS_STATUS update(PM_SPHERICAL* x, int n);    // Sph
    CMS_STATUS update(PM_CYLINDRICAL* x, int n);  // Cyl

    // rotation types
    CMS_STATUS update(PM_ROTATION_VECTOR* x, int n);  // Rot
    CMS_STATUS update(PM_ROTATION_MATRIX* x, int n);  // Mat
    CMS_STATUS update(PM_QUATERNION* x, int n);       // Quat
    CMS_STATUS update(PM_EULER_ZYZ* x, int n);        // Zyz
    CMS_STATUS update(PM_EULER_ZYX* x, int n);        // Zyx
    CMS_STATUS update(PM_RPY* x, int n);              // Rpy

    // pose types
    CMS_STATUS update(PM_POSE* x, int n);         // Pose
    CMS_STATUS update(PM_HOMOGENEOUS* x, int n);  // Hom

    /* comm protocol parameters shared by all protocols */
    int fatal_error_occurred;
    int consecutive_timeouts;
    CMS_HEADER header;                 /* Information to be stored in CMS buffer. */
    int queuing_enabled;               /* queue messages in the buffer */
    CMS_QUEUING_HEADER queuing_header; /* information for multi-slot
					   buffers.  */
    CMSMODE mode;                      /* This process is reading or writing? */
    long size;                         /* size of cms */
    long free_space;
    long max_message_size; /* size of cms buffer available for user */
    /* messages = size - CMS Header space */
    long max_encoded_message_size; /* Maximum size of message after
					   being encoded. */
    long guaranteed_message_space; /* Largest size message before being
					   encoded that can be guaranteed to
					   fit after xdr. */
    int neutral;                   /* neutral data format in buffer */

    CMS_STATUS status;               /* Status of the last CMS access. */
    void set_cms_status(CMS_STATUS); /* Catch changes in cms status.  */
    int spawn_server;

    /* Buffers for local copies of global buffer. */
    void* encoded_data; /* pointer to local copy of encoded data */
    int using_external_encoded_data;
    void set_encoded_data(void*, long _encoded_data_size);
    void* data;        /* pointer to local copy of data (raw) */
    void* subdiv_data; /* pointer to current subdiv; */

    /* Intersting Info Saved from the Configuration File. */
    char BufferName[CMS_CONFIG_LINELEN];
    char BufferHost[CMS_CONFIG_LINELEN];
    char ProcessName[CMS_CONFIG_LINELEN];
    char BufferLine[CMS_CONFIG_LINELEN];
    char ProcessLine[CMS_CONFIG_LINELEN];
    char ProcessHost[CMS_CONFIG_LINELEN];
    char buflineupper[CMS_CONFIG_LINELEN];
    char proclineupper[CMS_CONFIG_LINELEN];
    char PermissionString[CMS_CONFIG_LINELEN];
    int is_local_master;
    int force_raw;
    bool serial;
    int split_buffer; /* Will the buffer be split into two areas so 
				   that one area can be read while the other
				   is written to ? */
    char toggle_bit;
    int first_read_done;
    int first_write_done;
    int write_permission_flag;
    int read_permission_flag;
    unsigned long rpc_program_number;
    int tcp_port_number;
    int stcp_port_number;
    int udp_port_number;
    long buffer_number;
    int delete_totally;
    long total_messages_missed;
    long messages_missed_on_last_read;
    char* format_low_ptr;
    char* format_high_ptr;
    long format_size;
    int check_pointer(char* ptr, long bytes);
    int isserver;   /* Is the process a server. */
    int is_phantom; /* Is this a phantom CMS channel? */
    CMS_BUFFERTYPE BufferType;
    CMS_PROCESSTYPE ProcessType;
    CMS_REMOTE_PORT_TYPE remote_port_type;
    int pointer_check_disabled;

    CMSID in_buffer_id;               /* Last id read, used to determine if new. */
    void* encoded_header;             /* pointer to local copy of encoded header */
    void* encoded_queuing_header;     /* pointer to local copy of encoded
					   queue info */
    long encoded_header_size;         /* Dynamically determined size */
    long encoded_queuing_header_size; /* Dynamically determined size */

    /* Header Neutral Formatting Functions. */
    int encode_header();         /* header-> ENCODE-> encoded_header */
    int decode_header();         /* encoded_header -> DECODE -> header */
    int encode_queuing_header(); /* queuing_header ->
					   encoded_queuing_header */
    int decode_queuing_header(); /* encoded_queuing_header
					   ->queuing_header */
    /* XDR of ASCII */
    CMS_NEUTRAL_ENCODING_METHOD neutral_encoding_method;
    CMS_NEUTRAL_ENCODING_METHOD temp_updater_encoding_method;

public:
    /* Type of internal access. */
    CMS_INTERNAL_ACCESS_TYPE internal_access_type;
    PHYSMEM_HANDLE* handle_to_global_data;
    PHYSMEM_HANDLE* dummy_handle;
    int write_just_completed;
    CMSMODE read_mode;
    CMSMODE write_mode;
    int read_updater_mode;
    int write_updater_mode;
    CMSMODE last_im;
    /* data buffer stuff */

    CMS_STATUS check_id(CMSID id); /* Determine if the buffer is new. */
    friend class CMS_SERVER;
    friend class CMS_SERVER_HANDLER;

public:
    double timeout;
    long connection_number;
    long total_connections;
    CMS_UPDATER* updater;
    CMS_UPDATER* normal_updater;
    CMS_UPDATER* temp_updater;

private:
    unsigned long encode_state;       /* Store position for save, restore. */
    unsigned long decode_state;       /* Store position for save, restore. */
    void open(void);                  /* Allocate memory and intialize XDR streams */
    static int number_of_cms_objects; /* Used to decide when to initialize
					   and cleanup PC-NFS Toolkit DLLs */

public:
    double blocking_timeout;
    double min_compatible_version;
    int confirm_write;
    int disable_final_write_raw_for_dma;
    virtual const char* status_string(int);

    int total_subdivisions;
    int current_subdivision;
    long subdiv_size;
    int set_subdivision(int _subdiv);
    long encoded_data_size;
    long enc_max_size;
    long enable_diagnostics;
    CMS_DIAG_PROC_INFO* dpi;
    virtual CMS_DIAG_PROC_INFO* get_diag_proc_info();
    virtual void set_diag_proc_info(CMS_DIAG_PROC_INFO*);
    virtual void setup_diag_proc_info();
    virtual void calculate_and_store_diag_info(PHYSMEM_HANDLE* _handle, void*);
    virtual void internal_retrieve_diag_info(PHYSMEM_HANDLE* _handle, void*);
    CMS_DIAGNOSTICS_INFO* di;
    virtual CMS_DIAGNOSTICS_INFO* get_diagnostics_info();
    int first_diag_store;
    double pre_op_total_bytes_moved;
    double time_bias;
    int skip_area;
    unsigned long half_offset;
    long half_size;
    int fast_mode;
    long size_without_diagnostics;
    int disable_diag_store;
    long diag_offset;
    int last_id_side0;
    int last_id_side1;
    int use_autokey_for_connection_number;
    /* RCS_CMD_MSG, RCS_STAT_MSG stuff */

private:
    CMS(CMS& cms);  // Don't copy me.
};

class CMS_HOST_ALIAS_ENTRY
{
public:
    char host[64];
    char alias[64];
};

extern LinkedList* cmsHostAliases;

enum CMS_CONNECTION_MODE
{
    CMS_NORMAL_CONNECTION_MODE = 0,      /* all config file parameters are
					   honored. */
    CMS_FORCE_LOCAL_CONNECTION_MODE = 1, /* all connections are forced 
						   to be local */
    CMS_FORCE_REMOTE_CONNECTION_MODE = 2 /* all connections are forced 
						   to be remote */
};

extern CMS_CONNECTION_MODE cms_connection_mode;

extern char* cms_check_for_host_alias(char* in);
extern int cms_encoded_data_explosion_factor;
extern int cms_print_queue_free_space;
extern int cms_print_queue_full_messages;

#endif /* !defined(CMS_HH) */
